/* 
* File:   JoyMouse.cpp
* Author: Ben
*
* Created on April 28, 2013, 6:30 PM
*/

#include <cstdlib>
#include <string>
#include <unordered_map>
#include <iostream>
#include <windows.h>
#include <memory>
#include "Logger.h"
#include "Warning.h"
#include "Error.h"
#include "DirectInputEnvironment.h"
#include "RelativeMouseController.h"
#include "AbsoluteMouseController.h"
#include "KeyController.h"

using namespace std;

unordered_map<wstring, wstring> arguments;

const wstring argNames[] = {
	L"controller",
	L"relative",
	L"version",
	L"help"
};
const wstring version = L"JoyMouseCPP 0.2.0";
const wstring versionInfo = version + L"\n"
	L"Copyright (C) 2013 Ben Wolsieffer\n"
	L"MIT License <http://opensource.org/licenses/MIT>\n"
	L"This is open source software: you are free to change and redistribute it.\n"
	L"There is NO WARRANTY, to the extent permitted by law.";

const wstring helpMsg = L"A simple utility for controlling "
	L"a mouse with a joystick or other controller.\n"
	L"\n"
	L"Options:\n"
	L"-c, --controller CONTROLLER: The controller to use.\n"
	L"-r, --relative: Use relative mouse control mode(recommended); if not specified, absolute control mouse is used.\n"
	L"-v, --version: Print current version.\n"
	L"-h, --help: Show this help.";

const bool hasArg(const wstring &arg) {
	return arguments.find(arg) != arguments.cend();
}

const wstring getArg(const wstring &arg) {
	try{
		return arguments.at(arg);
	} catch(out_of_range&){
		return L"";
	}
}

controllers::Joystick* getJoystickByName(const std::vector<controllers::Joystick> &conts, const wstring& name){
	for(auto cont = conts.begin(); cont != conts.end(); ++cont) {
		if((cont->getName() == name) && (cont->getType() == controllers::Joystick::Type::kJoystick)) {
			return (controllers::Joystick*)&(*cont);
		}
	}
	return NULL;
}

controllers::Joystick* getFirstStick(const std::vector<controllers::Joystick> &conts){
	for(auto cont = conts.begin(); cont != conts.end(); ++cont) {
		if(cont->getType()==controllers::Joystick::Type::kJoystick) {
			return (controllers::Joystick*)&(*cont);
		}
	}
	return NULL;
}

void processArgs(wchar_t* args[], int length) {
	for (int n = 0; n < 4; n++) {
		const wstring name = argNames[n];
		const wstring longName = L"--" + name;
		const wstring shortName = longName.substr(1, 2);
		for (int a = 0; a < length; a++) {
			wstring arg = args[a];
			if (arg == longName || arg == shortName) {
				if (!hasArg(name)) {
					wstring value = L"";
					if (a + 1 < length) {
						value = args[a + 1];
						if (value.front() == '-') {
							value = L"";
						} else {
							a++;
						}
					}
					arguments.emplace(name, value);
				} else {
					logger::Logger::getLogger().log(logger::Warning::kArgumentIncludedTwice);
				}
			}
		}
	}
}

void run(MouseController& mc, controllers::Joystick* controller){
	KeyController kc(controller);
	for(;;) {
			controller->poll();
			mc.run();
			kc.run();
			Sleep(1);
		}
}

int wmain(int argc, wchar_t** argv) {
	processArgs(argv, argc);
	const bool showHelp = hasArg(L"help");
	const bool showVersion = hasArg(L"version");
	if (showHelp) {
		wcout << version << endl;
		wcout << helpMsg;
	} else if (showVersion) {
		wcout << versionInfo;
	} else {
		controllers::Joystick *controller;
		const auto conts = controllers::getJoysticks(); //fix pointer problems
		const bool relative = hasArg(L"relative");
		const wstring& controllerName = getArg(L"controller");
		if (controllerName != L"") {
			controller = getJoystickByName(*conts, controllerName);
			if (controller == NULL) {
				logger::Logger::getLogger().log(logger::Error::kInvalidJoystickName);
			}
		} else {
			controller = getFirstStick(*conts);
			if (controller == NULL) {
				logger::Logger::getLogger().log(logger::Error::kNoJoystickFound);
			}
		}
		logger::Logger::getLogger().log(L"Using joystick: " + controller->getName());
		controller->create();
		controller->acquire();

		/*ControllerManager::addDeviceController<RelativeMouseController>(RelativeMouseController(controller));

		ControllerManager::run();*/

		MouseController* mouseController = NULL;

		if(relative) {
			run(RelativeMouseController(controller), controller);
		} else {
			run(AbsoluteMouseController(controller), controller);
		}
	}
	system("PAUSE");
	return 0;
}
